package com.zhihaoliang.code

import com.zhihaoliang.code.bean.ItemType
import com.zhihaoliang.code.bean.RootType
import okio.buffer
import okio.source
import java.io.File
import java.io.FileFilter
import javax.xml.bind.JAXBContext

/**
 *用于代码的解析
 *
 * @author zhihaoliang
 * @date 2019/9/19 2:59 PM
 * @version：1.0.0
 */

const val USE_MSG = """
用法: java [-options] dir(文件夹路径) [args...]

其中选项包括-options:
    -android	  用于解析Android项目工程

其中选项包括args:
    -info	      显示解析的log
    """


fun readXml(context: String): RootType? {
    val jc = JAXBContext.newInstance(RootType::class.java)
    val u = jc.createUnmarshaller()
    return u.unmarshal(File(context)) as RootType
}


fun filterName(fileName: String, sets: Set<String>): Boolean {
    sets.forEach {
        if (fileName.endsWith(it, true)) {
            return !fileName.startsWith(".")
        }
    }
    return false
}



fun main(list: Array<String>) {
    if(list.size < 2){
        println(USE_MSG)
        return
    }

    val fileDir = File(list[1])

    if (!fileDir.isDirectory) {
        println(USE_MSG)
        return
    }


    var isInfo = false
    var isAndroid = false
    for (index in 1 until list.size){
        if("-android".equals(list[index],true)){
            isAndroid = true
        }

        if("-info".equals(list[index],true)){
            isInfo = true
        }
    }


    val rootType = readXml(list[0])

    val size = getItemSize(rootType!!.item!!, 0, isAndroid)
    val listCodeInfo = mutableListOf<CodeInfo>()
    val set = mutableSetOf<String>()
    for (index in 0 until size) {
        val codeInfo = getItem(rootType.item, index, isAndroid)
        listCodeInfo.add(codeInfo!!)
        set.add(codeInfo.itemType.suff)
    }


    val fileFilter = FileFilter { (it.isDirectory && it.name != "build" && !it.name.startsWith(".")) || filterName(it.name, set) }
    readDir(fileDir, listCodeInfo, isAndroid, isInfo, fileFilter)

    val sb = StringBuilder()
    listCodeInfo.forEach {
        printInf(it, sb)
    }
    println(sb.toString())
}

fun getItemSize(list: List<ItemType>, size: Int, isAndroid: Boolean): Int {
    var sizeNum = size

    list.forEach {
        sizeNum++
        if (isAndroid && it.item != null) {
            sizeNum = getItemSize(it.item, sizeNum, isAndroid)
        }
    }
    return sizeNum
}

fun getItem(list: List<ItemType>, size: Int, isAndroid: Boolean): CodeInfo? {
    if (isAndroid) {
        var sizeIndex = size
        var sizeNum = 0

        list.forEach {

            if (sizeIndex == sizeNum) {
                return CodeInfo(list[sizeIndex], 0, 0, 0, 0, 0)
            }

            sizeNum++
            if (it.item != null) {
                if (sizeNum + it.item.size - 1 >= sizeIndex) {
                    return CodeInfo(it.item[sizeIndex - sizeNum], 0, 0, 0, 0, 0, true).builderSuff(it.suff)
                } else {
                    sizeIndex -= it.item.size
                }
            }
        }
    } else {
        return CodeInfo(list[size], 0, 0, 0, 0, 0)
    }

    return null
}

fun printInf(codeInfo: CodeInfo, sb: java.lang.StringBuilder) {
    if (codeInfo.fileNum == 0) {
        return
    }

    sb.append("${codeInfo.itemType.inf} \n\r")
    sb.append("  文件个数：${codeInfo.fileNum} \n\r")
    sb.append("  代码行数：${codeInfo.lineNum} \n\r")
    sb.append("  有效代码：${codeInfo.eteLine} \n\r")
    sb.append("  注释行数：${codeInfo.atnLineNum} \n\r")
    sb.append("  空行   ：${codeInfo.emptyLine} \n\r")
    sb.append(" \n\r")
}

fun initCodeInfo(codeInfoList: MutableList<CodeInfo>, isAndroid: Boolean, file: File): CodeInfo? {
    val codeInfo = initCodeInfo(codeInfoList, file)
    if (isAndroid) {
        codeInfoList.forEach {
            if (file.name.endsWith(it.itemType.suff, true) && it.isChild && file.parentFile.name.startsWith(it.itemType.type)) {
                return it
            }
        }
    }
    return codeInfo
}

fun initCodeInfo(codeInfoList: MutableList<CodeInfo>, file: File): CodeInfo? {
    codeInfoList.forEach {
        if (!it.isChild && file.name.endsWith(it.itemType.suff)) {
            return it
        }
    }
    return null
}


fun readDir(file: File, codeInfoList: MutableList<CodeInfo>, isAndroid: Boolean, isInf: Boolean, fileFilter: FileFilter) {
    val childs = file.listFiles(fileFilter)
    if (isInf) {
        println("开始解析文件夹 : ${file.path}")
    }

    childs.forEach {
        if (it.isDirectory) {
            readDir(it, codeInfoList, isAndroid, isInf, fileFilter)
        } else {
            val codeInfo = initCodeInfo(codeInfoList, isAndroid, it)!!
            codeInfo.fileNum++
            readCode(it, codeInfo)
        }
    }
}


fun readCode(file: File, codeInfo: CodeInfo) {
    val buffer = file.inputStream().source().buffer()
    val isXml = file.name.endsWith(".xml", true)
    var indexMoreAn = 0
    while (true) {
        val line: String = buffer.readUtf8Line()?.trim() ?: break
        codeInfo.lineNum++

        if (line.isEmpty()) {
            codeInfo.emptyLine++
            continue
        }

        if (!isXml && line.indexOf("""//""") != -1) {
            codeInfo.atnLineNum++
            continue
        }

        if (moreLineAn(isXml, line)) {
            indexMoreAn++
        }

        if (indexMoreAn > 0) {
            codeInfo.atnLineNum++
        } else {
            codeInfo.eteLine++
        }

        if (moreLineAn(isXml, line, false)) {
            indexMoreAn--
        }

    }
}


fun moreLineAn(isXml: Boolean, line: String, isStart: Boolean = true): Boolean {
    if (isStart) {
        if (isXml) {
            return line.indexOf("""<!--""") != -1
        }

        return line.indexOf("""/*""") != -1
    }
    if (isXml) {
        return line.indexOf("""<!--""") != -1
    }

    return line.indexOf("""/*""") != -1

}

